using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace Polymod
{
    internal static class DictionaryHelpers
    {
        #region Get Or Return
        public static Value GetOrNew<Key, Value>(this IDictionary<Key, Value> dictionary, Key key)
            where Value : new()
        {
            return GetOr<Key, Value>(dictionary, key, () => new Value());
        }
        public static Value[] GetOrEmpty<Key, Value>(this IDictionary<Key, Value[]> dictionary, Key key)
        {
            return GetOr<Key, Value[]>(dictionary, key, () => new Value[0]);
        }
        public static Value GetOrDefault<Key, Value>(this IDictionary<Key, Value> dictionary, Key key)
            where Value : class
        {
            return GetOr<Key, Value>(dictionary, key, () => default(Value));
        }

        public static Value GetOr<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, Value valueIfNotExist)
        {
            return GetOr<Key, Value>(dictionary, key, () => valueIfNotExist);
        }

        public static Value GetOr<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, Func<Value> valueIfNotExist)
        {
            Value result;
            if (!dictionary.TryGetValue(key, out result)) return valueIfNotExist();
            return result;
        }
        #endregion

        #region GetOrCreate
        public static Value GetOrCreateNew<Key, Value>(this IDictionary<Key, Value> dictionary, Key key)
            where Value : new()
        {
            return GetOrCreate<Key, Value>(dictionary, key, () => new Value(), null);
        }
        public static Value GetOrCreateNew<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, object synclock)
            where Value : new()
        {
            return GetOrCreate<Key, Value>(dictionary, key, () => new Value(), synclock);
        }
        public static Value GetOrCreateDefault<Key, Value>(this IDictionary<Key, Value> dictionary, Key key)
            where Value : class
        {
            return GetOrCreate<Key, Value>(dictionary, key, () => default(Value), null);
        }
        public static Value GetOrCreateDefault<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, object synclock)
            where Value : class
        {
            return GetOrCreate<Key, Value>(dictionary, key, () => default(Value), synclock);
        }
        public static Value GetOrCreate<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, Value valueIfNotExist)
        {
            return GetOrCreate<Key, Value>(dictionary, key, () => valueIfNotExist, null);
        }
        public static Value GetOrCreate<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, Value valueIfNotExist, object synclock)
        {
            return GetOrCreate<Key, Value>(dictionary, key, () => valueIfNotExist, synclock);
        }
        public static Value GetOrCreate<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, Func<Value> valueIfNotExist)
        {
            return GetOrCreate<Key, Value>(dictionary, key, valueIfNotExist, null);
        }
        public static Value GetOrCreate<Key, Value>(this IDictionary<Key, Value> dictionary, Key key, Func<Value> valueIfNotExist, object synclock)
        {
            Value result;
            if (!dictionary.TryGetValue(key, out result))
            {
                if (synclock != null)
                {
                    lock (synclock)
                    {
                        if (!dictionary.TryGetValue(key, out result))
                        {
                            result = valueIfNotExist();
                            dictionary.Add(key, result);
                        }
                    }
                }
                else
                {
                    result = valueIfNotExist();
                    dictionary.Add(key, result);
                }
            }
            return result;
        }
        #endregion

    }
}
